/**
 * 
 */
package org.t2.game.server.module.player;

import java.util.List;
import java.util.TimeZone;

import org.gaming.fakecmd.annotation.InternalCmd;
import org.gaming.fakecmd.annotation.PlayerCmd;
import org.gaming.ruler.eventbus.EventBus;
import org.gaming.ruler.util.SensitiveUtil;
import org.gaming.service.advice.ResponseResult;
import org.gaming.service.asserts.Asserts;
import org.gaming.tool.DateTimeUtil;
import org.gaming.tool.GsonUtil;
import org.gaming.tool.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.t2.design.module.NameTable;
import org.t2.design.module.autogen.NameAbstractTable.NameCfg;
import org.t2.game.server.foundation.player.LogoutInternalMessage;
import org.t2.game.server.foundation.player.PlayerActorPool;
import org.t2.game.server.foundation.player.PlayerContext;
import org.t2.game.server.foundation.player.PlayerContextManager;
import org.t2.game.server.foundation.starting.ApplicationSettings;
import org.t2.game.server.module.backstage.BackstagePoster;
import org.t2.game.server.module.difchannel.DifChannelManager;
import org.t2.game.server.module.player.event.PlayerCreateEvent;
import org.t2.game.server.module.player.event.PlayerLoginEvent;
import org.t2.game.server.module.player.message.PlayerForbidMessage;
import org.t2.game.server.module.player.message.PlayerLoginMessage;
import org.t2.game.server.module.player.model.Player;
import org.t2.game.server.module.player.model.PlayerSwitch;
import org.t2.game.server.module.player.service.PlayerService;
import org.t2.game.server.module.player.service.PlayerSwitchService;
import org.t2.game.server.module.server.service.ServerInfoService;
import org.t2.game.server.module.tips.TipsCode;

import com.google.gson.reflect.TypeToken;

import pb.t2.protobuf.player.Player.HeartBeatRequest;
import pb.t2.protobuf.player.Player.HeartBeatResponse;
import pb.t2.protobuf.player.Player.PbLoginStatus;
import pb.t2.protobuf.player.Player.PbLogoutType;
import pb.t2.protobuf.player.Player.PbPlayer;
import pb.t2.protobuf.player.Player.PlayerChangeNickRequest;
import pb.t2.protobuf.player.Player.PlayerChangeNickResponse;
import pb.t2.protobuf.player.Player.PlayerLoginRequest;
import pb.t2.protobuf.player.Player.PlayerLoginResponse;
import pb.t2.protobuf.player.Player.PlayerLogoutMessage;
import pb.t2.protobuf.player.Player.PlayerLogoutRequest;
import pb.t2.protobuf.player.Player.PlayerUseGiftCodeRequest;
import pb.t2.protobuf.player.Player.PlayerUseGiftCodeResponse;
import pb.t2.protobuf.player.Player.ServerTimeRequest;
import pb.t2.protobuf.player.Player.ServerTimeResponse;
import sh.t2.share.apiutil.GameApi;
import sh.t2.share.module.giftcode.GiftCodeApiProtocol.GiftCodeRequest;
import sh.t2.share.module.giftcode.GiftCodeApiProtocol.GiftCodeResponse;

/**
 * @author YY
 */
@Controller
public class PlayerHandler {
	private static Logger logger = LoggerFactory.getLogger(PlayerHandler.class);
	
	@Autowired
	private PlayerService playerService;
	@Autowired
	private PlayerContextManager playerContextManager;
	@Autowired
	private ApplicationSettings applicationSettings;
	@Autowired
	private ServerInfoService serverInfoService;
	@Autowired
	private DifChannelManager difChannelManager;
	@Autowired
	private PlayerSwitchService playerSwitchService;
	@Autowired
	private BackstagePoster backstagePoster;
	
	@PlayerCmd(needLogin = false)
	public PlayerLoginResponse login(PlayerContext playerContext, PlayerLoginRequest request) {
		String openId = request.getOpenId().trim();
		Asserts.isTrue(!openId.equals(""), TipsCode.PLAYER_OPENID_EMPTY);
		
		logger.info("账号{}请求登录", openId);
		boolean serverMatch = applicationSettings.getGameServerIds().contains(request.getServerId());
		Asserts.isTrue(serverMatch, TipsCode.PLAYER_SERVER_WRONG, request.getServerId());
		
		//正在维护或者服务器还没有对外开放的时候，需要看看是不是白名单账号
        if (serverInfoService.isMaintain()) {
            //请求后台查看是不是白名单
        	if (!playerService.isWhite(request.getChannelId(), request.getOpenId())) {
	            PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
	            response.setLoginStatus(PbLoginStatus.MAINTAIN);
	            return response.build();
        	}
        }
        if(!serverInfoService.isOpen()) {
        	//请求后台查看是不是白名单
        	if (!playerService.isWhite(request.getChannelId(), request.getOpenId())) {
	            PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
	            response.setLoginStatus(PbLoginStatus.NOT_OPEN);
	            return response.build();
        	}
        }
        
        String ip = playerContext.getRemoteAddress();
        if (playerService.isForbidIp(ip)) {
        	//封IP
        	PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
            response.setLoginStatus(PbLoginStatus.FORBID);
            response.setForbidEndTime(DateTimeUtil.currMillis() + DateTimeUtil.ONE_WEEK_MILLIS);
            return response.build();
        }
		
		//验证登录
		difChannelManager.onLogin(request);
		
		Player player = playerService.getPlayerByOpenIdAndServerId(openId, request.getServerId());
		boolean isCreate = false;
		if(player == null) {
			try {
				player = new Player();
				
				String nick = null;
				for(int i = 0; i < 5; i++) {
					List<NameCfg> xings = NameTable.ins().getInTypeCollector(1);
					List<NameCfg> mings = NameTable.ins().getInTypeCollector(2);
					int xingIndex = RandomUtil.closeOpen(0, xings.size());
					int mingIndex = RandomUtil.closeOpen(0, mings.size());
					NameCfg xingCfg = xings.get(xingIndex);
					NameCfg mingCfg = mings.get(mingIndex);
					nick = xingCfg.getName() + mingCfg.getName();
					if(!playerService.hasSameNick(nick)) {
						break;
					}
				}
				if(playerService.hasSameNick(nick)) {
					nick = nick + randomChar();
				}
				player.setPlatformId(applicationSettings.getGameServerPlatformid());
				player.setOpenId(openId);
				player.setNick(nick);
				player.setServerId(request.getServerId());
				player.setCreateIp(ip);
				player.setChannelId(request.getChannelId());
				String deviceModel = request.getDeviceModel();
				if(deviceModel.length() > 255) {
					deviceModel = deviceModel.substring(0, 200);
				}
				player.setDeviceModel(deviceModel);
				player.setNewComer(request.getNewComer());
				playerService.insert(player);
				isCreate = true;
			} catch (Exception e) {
				logger.error("创号异常", e);
				//预防账号ID重复
				player = playerService.getPlayerByOpenIdAndServerId(openId, request.getServerId());
			}
		}
		
		if(player.getForbidEndTime() > DateTimeUtil.currMillis()) {
			PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
            response.setLoginStatus(PbLoginStatus.FORBID);
            response.setForbidEndTime(player.getForbidEndTime());
            return response.build();
		}
		
		PlayerSwitch destPlayerSwitch = playerSwitchService.getDestSwitch(player.getId());
		if(destPlayerSwitch != null) {
			PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
            response.setLoginStatus(PbLoginStatus.SWITCHING);
            return response.build();
		}
		
		PlayerSwitch oriPlayerSwitch = playerSwitchService.getOriSwitch(player.getId());
		if(oriPlayerSwitch != null) {
			//账号切换
			player = playerService.getEntity(oriPlayerSwitch.getDestPlayerId());
		}
		
		playerContext.setId(player.getId());
        playerContext.setServerId(player.getServerId());
        playerContext.setOpenId(player.getOpenId());
        playerContext.setActor(PlayerActorPool.getActor(player.getId()));
        playerContext.setActorReady(true);
        
        playerContext.tell(PlayerLoginMessage.of(playerContext, request, isCreate));
    	
    	PlayerLoginResponse.Builder response = PlayerLoginResponse.newBuilder();
        response.setLoginStatus(PbLoginStatus.NORMAL);
        response.setServerTime(DateTimeUtil.currMillis());
        response.setOffsetUtc(TimeZone.getDefault().getOffset(DateTimeUtil.currMillis()));
        response.setOpenDay(serverInfoService.getOpenedDay());
        response.setPlayer(build(player));
        return response.build();
	}
	
	public PbPlayer build(Player player) {
		PbPlayer.Builder builder = PbPlayer.newBuilder();
		builder.setId(player.getId());
		builder.setNick(player.getNick());
		return builder.build();
	}
	
	@InternalCmd
	public void handle(PlayerLoginMessage message) {
		PlayerContext playerContext = message.getPlayerContext();
		PlayerLoginRequest request = message.getRequest();
		
        Asserts.isTrue(playerContext.isActorReady(), TipsCode.PLAYER_NOT_READY);

        Player player = playerService.getEntity(playerContext.getId());

        PlayerContext oldContext = playerContextManager.getContext(playerContext.getId());
        if (oldContext != null && playerContext != oldContext) {
            logger.info("玩家{}-{}异地登录，注销旧连接ID：{}", player.getId(), player.getOpenId(), oldContext.getChannel().id());
            LogoutInternalMessage logoutMessage = new LogoutInternalMessage();
            logoutMessage.playerContext = oldContext;
            logoutMessage.logoutType = PbLogoutType.ANOTHER_LOGIN;
            logoutMessage.lastLoginTime = player.getLastLoginTime();
            oldContext.tell(logoutMessage);
        }

        player.setOnline(true);
        player.setLastLoginTime(DateTimeUtil.currMillis());
        player.setLoginIp(playerContext.getRemoteAddress());
        String deviceModel = request.getDeviceModel();
		if(deviceModel.length() > 255) {
			deviceModel = deviceModel.substring(0, 200);
		}
		player.setDeviceModel(deviceModel);
        playerService.update(player);
        
        //添加到在线缓存
        playerContext.setLogin(true);
        playerContextManager.addToOnline(playerContext);
        playerService.addToReport(player);
        
        if(message.isCreate()) {
        	EventBus.post(PlayerCreateEvent.of(player));
        } else {
        	EventBus.post(PlayerLoginEvent.of(player));
        }
	}
	
	@PlayerCmd
    public void logout(PlayerContext playerContext, PlayerLogoutRequest request) {
        LogoutInternalMessage logoutMessage = new LogoutInternalMessage();
        logoutMessage.playerContext = playerContext;
        logoutMessage.logoutType = PbLogoutType.PLAYER_LOGOUT;
        playerContext.tell(logoutMessage);
    }
	
	@InternalCmd
	public void handle(LogoutInternalMessage message) {
		PlayerContext playerContext = message.playerContext;
        if(playerContext.getId() <= 0) {
        	//只是连接了服务器，但并没有进行登录操作
        	playerContext.closeChannel();
    		return;
    	}
        if (playerContext.getLogoutType() != null) {
            logger.info("玩家{}连接已经断开，登出方式：{}，连接ID：{}", playerContext.getId(), playerContext.getLogoutType(), playerContext.getChannel().id());
            return;
        }

        Player player = playerService.getEntity(playerContext.getId());
        if (message.logoutType != PbLogoutType.ANOTHER_LOGIN && playerContext.getId() > 0) {
            player.setOnline(false);
        }
        player.setLastLogoutTime(DateTimeUtil.currMillis());
        playerService.update(player);
        
        playerContext.setActorReady(false);
        playerContext.setLogin(false);
        playerContext.setLogoutType(message.logoutType);
        playerContextManager.removeFromOnline(playerContext);
        playerService.addToReport(player);

        PlayerLogoutMessage.Builder builder = PlayerLogoutMessage.newBuilder();
        builder.setLogoutType(message.logoutType);
        if (message.forbidEndTime > 0) {
            builder.setForbidEndTime(message.forbidEndTime);
        }
        playerContext.write(PlayerLogoutMessage.Proto.ID, builder.build());

        playerContext.closeChannel();

        logger.info("玩家{}登出游戏，登出方式：{}，连接ID：{}", playerContext.getId(), playerContext.getLogoutType(), playerContext.getChannel().id());
	}
	
	@PlayerCmd
    public HeartBeatResponse heartBeat(PlayerContext playerContext, HeartBeatRequest request) {
        HeartBeatResponse.Builder builder = HeartBeatResponse.newBuilder();
        builder.setServerTime(DateTimeUtil.currMillis());
        return builder.build();
    }
	
	@InternalCmd
    public void forbid(PlayerForbidMessage message) {
        Player player = playerService.getEntity(message.getPlayerId());

        player.setForbidEndTime(message.getForbidEndTime());
        playerService.update(player);

        if (DateTimeUtil.currMillis() < player.getForbidEndTime()) {
            PlayerContext playerContext = playerContextManager.getContext(player.getId());
            if (playerContext != null) {
                LogoutInternalMessage logoutMessage = new LogoutInternalMessage();
                logoutMessage.playerContext = playerContext;
                logoutMessage.logoutType = PbLogoutType.FORBID_LOGIN;
                logoutMessage.forbidEndTime = player.getForbidEndTime();
                playerContext.tell(logoutMessage);
            }
        } else {
            //解封时玩家必定不在线，这里不需要做消息通知
        }
    }
	
	@PlayerCmd
	public PlayerChangeNickResponse changeNick(PlayerContext playerContext, PlayerChangeNickRequest request) {
		String nick = request.getNick().trim();
		Asserts.isTrue(!"".equals(nick), TipsCode.PLAYER_NICK_EMPTY);
		String sensitive = SensitiveUtil.has(request.getNick());
		Asserts.isTrue(sensitive == null, TipsCode.PLAYER_NICK_SENSITIVE);
		Asserts.isTrue(!playerService.hasSameNick(nick), TipsCode.PLAYER_NICK_SAME);
		
		Player player = playerService.getEntity(playerContext.getId());
		String oldNick = player.getNick();
		player.setNick(nick);
		playerService.update(player);
		playerService.changeNick(player, oldNick);
		
		PlayerChangeNickResponse.Builder response = PlayerChangeNickResponse.newBuilder();
		response.setNick(player.getNick());
		return response.build();
	}
	
	public String randomChar() {
		String[] postfix = new String[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
				"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
		int index = RandomUtil.closeOpen(0, postfix.length);
		return postfix[index];
	}
	
	@PlayerCmd
	public PlayerUseGiftCodeResponse useGiftCode(PlayerContext playerContext, PlayerUseGiftCodeRequest request) {
		GiftCodeRequest apiRequest = new GiftCodeRequest();
		apiRequest.playerId = playerContext.getId();
		apiRequest.code = request.getCode();
		String apiResponse = backstagePoster.jsonPost(GameApi.useGiftCode, apiRequest);
		
		ResponseResult<GiftCodeResponse> result = GsonUtil.parseJson(apiResponse, TypeToken.getParameterized(ResponseResult.class, GiftCodeResponse.class));
		GiftCodeResponse dataRes = result.getData();
		if(dataRes.tipsCode == 0) {
			//发奖
		} else if(dataRes.tipsCode == 1) {
			Asserts.isTrue(false, TipsCode.GIFTCODE_EXPIRE);
		} else if(dataRes.tipsCode == 2) {
			Asserts.isTrue(false, TipsCode.GIFTCODE_USED);
		} else if(dataRes.tipsCode == 3) {
			Asserts.isTrue(false, TipsCode.GIFTCODE_USED_SAME);
		}
		PlayerUseGiftCodeResponse.Builder response = PlayerUseGiftCodeResponse.newBuilder();
		response.setRewards(dataRes.rewards);
		return response.build();
	}
	
	@PlayerCmd
	public ServerTimeResponse serverTime(PlayerContext playerContext, ServerTimeRequest request) {
		ServerTimeResponse.Builder response = ServerTimeResponse.newBuilder();
		response.setServerTime(DateTimeUtil.currMillis());
		response.setOffsetUtc(TimeZone.getDefault().getOffset(DateTimeUtil.currMillis()));
		return response.build();
	}
}
